/*

This file is part of Rooms3D.

Copyright (C) 2008 Pixecur Technologies Incorporated (Esotera).
Visit Esotera at http://www.esoteras3.com
 
Rooms3D is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
Rooms3D is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Rooms3D; see the file COPYING.  If not, write to 
Pixecur Technologies, 213-31 Peet Street, St. John's, NL, A1B 3W8. 
 
Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
 
*/

package com.datasentinel.rooms.ui.sh3d;

import java.awt.Component;
import java.awt.Image;
import java.awt.Window;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.image.RenderedImage;
import java.io.File;
import java.util.List;

import javax.imageio.ImageIO;
import javax.media.j3d.PickInfo;
import javax.swing.SwingUtilities;
import javax.vecmath.Point3d;

import com.datasentinel.rooms.file.FolderMonitor;
import com.datasentinel.rooms.file.sh3d.FilePieceOfFurniture;
import com.datasentinel.rooms.file.sh3d.FilePieceOfFurnitureBuilder;
import com.datasentinel.rooms.file.sh3d.HomeFilePieceOfFurniture;
import com.datasentinel.rooms.util.sh3d.FurnitureUtils;
import com.datasentinel.util.URIList;
import com.eteks.sweethome3d.model.FurnitureEvent;
import com.eteks.sweethome3d.model.FurnitureListener;
import com.eteks.sweethome3d.model.Home;
import com.eteks.sweethome3d.model.PieceOfFurniture;
import com.eteks.sweethome3d.model.Wall;

/**
 * @author rickp
 * 
 */
abstract public class CustomHomeComponent3DDropTargetAdapter extends DropTargetAdapter
{
	private FurnitureListener furnitureListener = null;
	private HomeFilePieceOfFurniture homePiece = null;
	private PickInfo wallPickInfo = null;
	private Home home;
	private FurnitureUtils furnitureUtils;

	public CustomHomeComponent3DDropTargetAdapter(Home newHome)
	{
		super();
		home = newHome;
		furnitureUtils = new FurnitureUtils(home);
		furnitureListener = new FurnitureListener() {
			@Override
			public void pieceOfFurnitureChanged(FurnitureEvent furnitureEvent)
			{
				/*
				 * may need to update furniture's position relative to mouse
				 * should the furniture geometry change during drag
				 */
				if (FurnitureEvent.Type.UPDATE == furnitureEvent.getType())
				{
					PieceOfFurniture piece = furnitureEvent.getPieceOfFurniture();
					if (piece.equals(homePiece))
					{
						updateFurniturePositionFromWallPickInfo();
					}
				}
			}
		};
	}

	@Override
	public void dragEnter(DropTargetDragEvent dtde)
	{
		if (homePiece != null)
		{
			throw new IllegalStateException("furniture piece already set");
		}
		FolderMonitor.getInstance().pause(true);
		home.addFurnitureListener(furnitureListener);
		tryToBringAppToFront(dtde);
		wallPickInfo = updateWallPickInfoFromDrag(dtde);
		initializeFurniture(dtde);
	}

	@Override
	public void dragOver(DropTargetDragEvent dtde)
	{
		wallPickInfo = updateWallPickInfoFromDrag(dtde);
		if (homePiece != null)
			home.setPieceOfFurnitureVisible(homePiece, null == wallPickInfo?false:true);
		updateFurniturePositionFromWallPickInfo();
		
		if(wallPickInfo == null)
			dtde.rejectDrag();
		else
			dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
	}

	@Override
	public void drop(DropTargetDropEvent dtde)
	{
		resetDragState(dtde);
	}

	@Override
	public void dragExit(DropTargetEvent dte)
	{
		resetDragState(dte);
		home.removeFurnitureListener(furnitureListener);
	}
	
	protected HomeFilePieceOfFurniture getHomeFilePieceOfFurniture()
	{
		return homePiece;
	}
	
	private void resetDragState(DropTargetEvent dte)
	{
		if (null != homePiece && !homePiece.isVisible())
		{
			home.deletePieceOfFurniture(homePiece);
		}
		homePiece = null;
		wallPickInfo = null;
		FolderMonitor.getInstance().pause(false);
	}

	private void tryToBringAppToFront(DropTargetDragEvent dtde)
	{
		final Component c = dtde.getDropTargetContext().getComponent();
		SwingUtilities.invokeLater(new Runnable() {
			public void run()
			{
				Window window = SwingUtilities.getWindowAncestor(c);
				if (window instanceof Window && !window.isActive())
				{
					window.requestFocusInWindow();
					window.toFront();
				}
			}
		});
	}

	abstract protected PickInfo updateWallPickInfoFromDrag(DropTargetDragEvent dtde);

	private void updateFurniturePositionFromWallPickInfo()
	{
		if (wallPickInfo instanceof PickInfo)
		{
			if (homePiece != null)
			{
				home.setPieceOfFurnitureVisible(homePiece, true);
			}
			Point3d closestIntersectionPoint = wallPickInfo.getClosestIntersectionPoint();
			if (closestIntersectionPoint instanceof Point3d)
			{
				Wall wall = (Wall)wallPickInfo.getNode().getParent().getUserData();
				furnitureUtils.placeFurnitureOnWall(homePiece, wall, closestIntersectionPoint);
			}
		}
		else if (homePiece != null)
		{
			home.setPieceOfFurnitureVisible(homePiece, false);
		}
	}

	private void initializeFurniture(DropTargetDragEvent dtde)
	{
		try
		{
			File file = null;
			if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
			{
				Object transferable = dtde.getTransferable().getTransferData(
						DataFlavor.javaFileListFlavor);
				List<?> files = (List<?>)transferable;
				Object first = files.get(0);
				if (first instanceof File)
					file = (File)first;
			}
			else if (dtde.isDataFlavorSupported(DataFlavor.imageFlavor))
			{
				RenderedImage image = (RenderedImage)dtde.getTransferable().getTransferData(
							DataFlavor.imageFlavor);
				file = File.createTempFile("drp", ".png");
				file.deleteOnExit();
				ImageIO.write(image, "png", file);
			}
			else if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor))
			{
				String transferData = (String)dtde.getTransferable().getTransferData(
						DataFlavor.stringFlavor);
				URIList uriList = new URIList(transferData);
				file = new File(uriList.get(0));
			}
			
			FilePieceOfFurniture newPiece = null;
			if (file != null)
				newPiece = (FilePieceOfFurniture)new FilePieceOfFurnitureBuilder(file).build();
			else
				return;
			homePiece = new HomeFilePieceOfFurniture(newPiece);
			home.addPieceOfFurniture(homePiece);
			updateFurniturePositionFromWallPickInfo();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}
